home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 42 / Amiga Format AFCD42 (Issue 126, Aug 1999).iso / -serious- / comms / other / slrn / slrn_src / src / group.c < prev    next >
C/C++ Source or Header  |  1999-05-14  |  63KB  |  2,622 lines

  1. /* -*- mode: C; mode: fold -*- */
  2.  
  3. /* Copyright (c) 1998 John E. Davis (davis@space.mit.edu)
  4.  *
  5.  * This file is part of slrn.
  6.  *
  7.  * Slrn is free software; you can redistribute it and/or modify it
  8.  * under the terms of the GNU General Public License as published by the
  9.  * Free Software Foundation; either version 2, or (at your option) any
  10.  * later version.
  11.  * 
  12.  * Slrn is distributed in the hope that it will be useful, but WITHOUT
  13.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15.  * for more details.
  16.  * 
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with Slrn; see the file COPYING.  If not, write to the Free
  19.  * Software Foundation, 59 Temple Place - Suite 330, 
  20.  * Boston, MA  02111-1307, USA.
  21.  */
  22.  
  23. #include "config.h"
  24. #include "slrnfeat.h"
  25.  
  26. /*{{{ Include files */
  27.  
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <time.h>
  31. #ifndef VMS
  32. # include <sys/types.h>
  33. # include <sys/stat.h>
  34. #else
  35. # include "vms.h"
  36. #endif
  37.  
  38. #include <signal.h>
  39.  
  40. #ifdef HAVE_STDLIB_H
  41. # include <stdlib.h>
  42. #endif
  43.  
  44. #ifdef HAVE_UNISTD_H
  45. # include <unistd.h>
  46. #endif
  47.  
  48.  
  49. #include <slang.h>
  50. #include "jdmacros.h"
  51.  
  52. #include "slrn.h"
  53. #include "group.h"
  54. #include "art.h"
  55. #include "misc.h"
  56. #include "post.h"
  57. #include "server.h"
  58. #include "hash.h"
  59. #include "score.h"
  60. #include "menu.h"
  61. #include "util.h"
  62. #include "startup.h"
  63. #include "slrndir.h"
  64.  
  65. /*}}}*/
  66.  
  67. /*{{{ Global Variables */
  68.  
  69. int Slrn_Query_Group_Cutoff = 100;
  70. int Slrn_Groups_Dirty;           /* greater than 0 if need to write newsrc */
  71. int Slrn_List_Active_File = 0;
  72. int Slrn_Use_Xgtitle = 0;
  73. int Slrn_Write_Newsrc_Flags = 0;       /* if 1, do not save unsubscribed 
  74.                     * if 2, do not save new unsubscribed.
  75.                     */
  76.  
  77. int Slrn_Display_Cursor_Bar;
  78. int Slrn_Group_Description_Column = 40;/* column where group descr start */
  79. char *Slrn_Group_Help_Line;
  80.  
  81. Slrn_Group_Type *Slrn_Group_Current_Group;
  82. static SLscroll_Window_Type Group_Window;
  83. static Slrn_Group_Type *Groups;
  84.  
  85. int Slrn_Group_Display_Descriptions = 1;
  86. int Slrn_No_Backups = 0;
  87. int Slrn_Prompt_Next_Group = 1;
  88.  
  89. int Slrn_Unsubscribe_New_Groups = 0;
  90.  
  91. SLKeyMap_List_Type *Slrn_Group_Keymap;
  92. int *Slrn_Prefix_Arg_Ptr;
  93.  
  94. /*}}}*/
  95. /*{{{ Static Variables */
  96.  
  97. #define GROUP_HASH_TABLE_SIZE 1250
  98. static Slrn_Group_Type *Group_Hash_Table [GROUP_HASH_TABLE_SIZE];
  99.  
  100. static unsigned int Last_Cursor_Row;
  101. static int Groups_Hidden;           /* if true, hide groups with no arts */
  102.  
  103. typedef struct Unsubscribed_Slrn_Group_Type
  104. {
  105.    char name[MAX_GROUP_NAME_LEN + 1];
  106.    struct Unsubscribed_Slrn_Group_Type *next;
  107. }
  108. Unsubscribed_Slrn_Group_Type;
  109.  
  110. static Unsubscribed_Slrn_Group_Type *Unsubscribed_Groups;
  111.  
  112. /*}}}*/
  113.  
  114. /*{{{ Forward Function Declarations */
  115.  
  116. static void group_update_screen (void);
  117. static void group_quick_help (void);
  118. static void save_newsrc_cmd (void);
  119.  
  120. /*}}}*/
  121.  
  122. /*{{{ Functions that deal with Group Range */
  123.  
  124.  
  125. /* Note: This routine is NOT very robust.  It assumes that this function
  126.  * is called in an ordered way such that the implied range is always increasing.
  127.  * This is why the range is re-built in art.c:update_ranges.  Yes, it is ugly.
  128.  * See also slrn_group_mark_article_as_read for something more random.
  129.  */
  130. void slrn_add_group_ranges (Slrn_Group_Type *g, int min, int max) /*{{{*/
  131. {
  132.    Slrn_Range_Type *r, *next;
  133.    int unread;
  134.    
  135.    if ((max < min) || (g == NULL)) return;
  136.    
  137.    /* The first one is range of articles on server so expand max to cover
  138.     * the range of articles nolonger available.
  139.     */
  140.    next = &g->range;
  141.    if (max < next->min) 
  142.      {
  143.     /* If we have already expanded the range up to range currently available
  144.      * at server and we are now trying to add another range below available
  145.      * range, do not bother.
  146.      */
  147.     if (next->next != NULL) return;
  148.     max = next->min - 1;
  149.      }
  150.    
  151.    /* Count number unread */
  152.    unread = next->max;
  153.    while (next->next != NULL)
  154.      {
  155.     next = next->next;
  156.     unread -= next->max - next->min + 1;
  157.      }
  158.    
  159.    /* check to see if a merge is possible */
  160.    if ((min <= next->max + 1)
  161.        && (next != &g->range))
  162.      {
  163.     next->max = max;
  164.      }
  165.    else
  166.      {
  167.     r = (Slrn_Range_Type *) slrn_safe_malloc (sizeof(Slrn_Range_Type));
  168.     
  169.     r->next = next->next;
  170.     next->next = r;
  171.     r->prev = next;
  172.     
  173.     r->min = min;
  174.     r->max = max;
  175.     
  176.     /* For this case, min should be 1 */
  177.     if (next == &g->range)
  178.       {
  179.          min = r->min = 1;
  180.       }
  181.      }
  182.    
  183.    unread -= max - min + 1;
  184.    
  185.    if (unread < 0) unread = 0;
  186.    g->unread = unread;
  187.    Slrn_Groups_Dirty = 1;
  188. }
  189.  
  190. /*}}}*/
  191.  
  192. static void group_mark_article_as_read (Slrn_Group_Type *g, long num) /*{{{*/
  193. {
  194.    Slrn_Range_Type *r, *r1, *newr;
  195.    
  196.    r1 = &g->range;
  197.    if (r1->max < num)  /* not at server yet so update our data */
  198.      {
  199.     r1->max = num;
  200.     g->unread += 1;
  201.      }
  202.    
  203.    r = r1->next;
  204.    
  205.    while (r != NULL)
  206.      {
  207.     /* Already read */
  208.     if ((num <= r->max) && (num >= r->min)) return;
  209.     if (num < r->min) break;
  210.     r1 = r;
  211.     r = r->next;
  212.      }
  213.    
  214.    if (g->unread > 0) g->unread -= 1;
  215.    Slrn_Groups_Dirty = 1;
  216.    if ((r != NULL) && (r->min == num + 1))
  217.      {
  218.     r->min = num;
  219.     return;
  220.      }
  221.    
  222.    if ((r1->max + 1 == num) && (r1 != &g->range))
  223.      {
  224.     r1->max = num;
  225.     return;
  226.      }
  227.    
  228.    newr = (Slrn_Range_Type *) slrn_safe_malloc (sizeof (Slrn_Range_Type));
  229.    
  230.    newr->min = newr->max = num;
  231.    newr->next = r;
  232.    if (r != NULL) r->prev = newr;
  233.    newr->prev = r1;
  234.    r1->next = newr;
  235. }
  236.  
  237. /*}}}*/
  238.  
  239. void slrn_mark_article_as_read (char *group, long num) /*{{{*/
  240. {
  241.    Slrn_Group_Type *g;
  242.    unsigned long hash;
  243.    
  244.    if (group == NULL)
  245.      {
  246.     group_mark_article_as_read (Slrn_Group_Current_Group, num);
  247.     return;
  248.      }
  249.    
  250.    hash = slrn_compute_hash ((unsigned char *) group,
  251.                  (unsigned char *) group + strlen (group));
  252.    
  253.    g = Group_Hash_Table[hash % GROUP_HASH_TABLE_SIZE];
  254.    
  255.    while (g != NULL)
  256.      {
  257.     if ((g->hash == hash) && !strcmp (group, g->name))
  258.       {
  259.          /* If it looks like we have read this group, mark it read. */
  260.          if (((g->flags & GROUP_UNSUBSCRIBED) == 0)
  261.          || (g->range.next != NULL))
  262.            group_mark_article_as_read (g, num);
  263.          
  264.          break;
  265.       }
  266.     g = g->hash_next;
  267.      }
  268. }
  269.  
  270. /*}}}*/
  271.  
  272. static int group_sync_group_with_server (Slrn_Group_Type *g, int *minp, int *maxp) /*{{{*/
  273. {
  274.    int min, max, n, max_available;
  275.    char *group;
  276.    Slrn_Range_Type *r;
  277.    int status;
  278.    
  279.    if (g == NULL) return -1;
  280.    
  281.    group = g->name;
  282.    
  283.    slrn_message_now ("Selecting %s ...", group);
  284.    
  285.    status = Slrn_Server_Obj->sv_select_group (group, minp, maxp);
  286.    if (status == -1)
  287.      return -1;
  288.    
  289.    if (status != OK_GROUP)
  290.      {
  291.     g->flags |= GROUP_UNSUBSCRIBED;
  292.     slrn_error ("This group appears to be bogus.");
  293.     return -1;
  294.      }
  295.  
  296.    min = *minp;
  297.    max = *maxp;
  298.    
  299.    if (max == 0)
  300.      {
  301.     int nmax, nmin;
  302.     
  303.     nmax = g->range.max;
  304.     nmin = g->range.min;
  305.     
  306.     /* Server database inconsistent. */
  307.     for (n = nmin; n <= nmax; n++)
  308.       group_mark_article_as_read (g, n);
  309.     
  310.     /* g->unread = 0; */
  311.     slrn_message ("No articles to read.");
  312.     Slrn_Full_Screen_Update = 1;
  313.     Slrn_Groups_Dirty = 1;
  314.     return -1;
  315.      }
  316.    
  317.    g->range.min = min;
  318.    if (max < g->range.max)
  319.      {
  320.     /* There is only one way for this to happen that I am aware of:
  321.      * an article has been cancelled-- update the ranges.
  322.      */
  323.     int nmax = g->range.max;
  324.     for (n = max + 1; n <= nmax; n++)
  325.       group_mark_article_as_read (g, n);
  326.      }
  327.    else g->range.max = max;
  328.  
  329.    /* In case more articles arrived at the server between the time that 
  330.     * slrn was first started and when the server was just queried, update
  331.     * the ranges of read/unread articles.
  332.     */
  333.    
  334.    max_available = g->range.max - g->range.min + 1;
  335.    
  336.    r = &g->range;
  337.    if (r->next != NULL)
  338.      {
  339. #if 0
  340.     /* stesch@parsec.rhein-neckar.de (Stefan Scholl) suggests moving this
  341.      * below to avoid posibility of n < 0.
  342.      */
  343.     if (r->next->min <= r->min)
  344.       r->next->min = 1;
  345. #endif    
  346.     n = r->max;
  347.     while (r->next != NULL)
  348.       {
  349.          r = r->next;
  350.          n -= r->max - r->min + 1;
  351.       }
  352.     if (n < 0) n = 0;
  353.     if (n > max_available) n = max_available;
  354.     g->unread = n;
  355. #if 1
  356.     r = &g->range;
  357.     if (r->next->min <= r->min)
  358.       r->next->min = 1;
  359. #endif
  360.      }
  361.    
  362.    return 0;
  363. }
  364.  
  365.  
  366.  
  367. /*}}}*/
  368.  
  369. static void free_group_ranges (Slrn_G